package l1j.server.server.model.Instance;


import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

import l1j.opqlo.NewSystem.XMas;
import l1j.opqlo.NewSystem.goblinThread;
import l1j.opqlo.NewSystem.Attack.L1AttackModel;
import l1j.opqlo.NewSystem.Attack.L1AttackPc;
import l1j.opqlo.NewSystem.Extension.TreasureMaster;
import l1j.opqlo.Server.L1World.L1World;
import l1j.server.Config;
import l1j.server.Thread.GeneralThreadPool;
import l1j.server.server.ActionCodes;
import l1j.server.server.datatables.ItemTable;
import l1j.server.server.datatables.NPCTalkDataTable;
import l1j.server.server.datatables.SkillsTable;
import l1j.server.server.datatables.UBTable;
import l1j.server.server.model.L1Character;
import l1j.server.server.model.L1Location;
import l1j.server.server.model.L1Magic;
import l1j.server.server.model.L1NpcTalkData;
import l1j.server.server.model.L1Object;
import l1j.server.server.model.L1Teleport;
import l1j.server.server.model.L1UltimateBattle;
import l1j.server.server.model.drop.DropShare;
import l1j.server.server.model.drop.DropShareExecutor;
import l1j.server.server.model.drop.SetDrop;
import l1j.server.server.model.drop.SetDropExecutor;
import l1j.server.server.model.skill.L1SkillId;
import l1j.server.server.model.skill.L1SkillUse;
import l1j.server.server.serverpackets.S_DoActionGFX;
import l1j.server.server.serverpackets.S_HPMeter;
import l1j.server.server.serverpackets.S_NPCPack;
import l1j.server.server.serverpackets.S_NPCTalkReturn;
import l1j.server.server.serverpackets.S_RemoveObject;
import l1j.server.server.serverpackets.S_ServerMessage;
import l1j.server.server.serverpackets.S_SkillBrave;
import l1j.server.server.serverpackets.S_TrueTarget;
import l1j.server.server.templates.L1Npc;
import l1j.server.server.templates.L1Skills;
import l1j.server.server.utils.CalcExp;
import l1j.server.server.utils.Random;
import l1j.server.server.datatables.MonsterEnhanceTable;//TODO 怪物強化系統by missu0524
import l1j.server.server.serverpackets.S_SystemMessage;//TODO 怪物強化系統by missu0524
import l1j.william.MobTalk;//TODO 怪物說話阿傑 code 1/4
import static l1j.server.server.model.skill.L1SkillId.ATTRACT_MONSTER; // opqlo 吸怪狀態


public class L1MonsterInstance extends L1NpcInstance {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	private static Logger _log = Logger.getLogger(L1MonsterInstance.class.getName());

	private boolean _storeDroped; // ドロップアイテムの読込が完了したか


	// アイテム使用処理
	@Override
	public void onItemUse() {
		if (!isActived() && _target != null) {
			useItem(USEITEM_HASTE, 40); // ４０％の確率でヘイストポーション使用
			if (getCurrentHp() * 100 / getMaxHp() < 40) { // ＨＰが４０％きったら
				useItem(USEITEM_HEAL, 50); // ５０％の確率で回復ポーション使用
			}
			// アイテムじゃないけどドッペル処理
			if (getNpcTemplate().is_doppel() && _target instanceof L1PcInstance) {
				L1PcInstance targetPc = (L1PcInstance) _target;
				setName(_target.getName());
				setNameId(_target.getName());
				setTitle(_target.getTitle());
				setTempLawful(_target.getLawful());
				// TODO 變怪可直接攻擊 by 0919162173
				// 刪除setTempCharGfx(targetPc.getClassId());
				// 刪除setGfxId(targetPc.getClassId());
				//TODO 變形怪變身幻術師變成空手攻擊by9001183ex
				final boolean special = false;

				int gfxid = 5853;//TODO 王族

				switch (targetPc.getClassId()) {
				case 1: {//TODO 公主
					gfxid +=1;
				}
				break;
				case 61: { //TODO 男騎
					gfxid +=2;
				}
				break;
				case 48: { //TODO 女騎
					gfxid +=3;
				}
				break;
				case 138: { //TODO 男妖
					gfxid +=4;
				}
				break;
				case 37: { //TODO 女妖
					gfxid +=5;
				}
				break;
				case 734: { //TODO 男法
					gfxid +=6;
				}
				break;
				case 1186: { //TODO 女法
					gfxid +=7;
				}
				break;
				case 2786: { //TODO 男黑
					gfxid +=8;
				}
				break;
				case 2796: { //TODO 女黑
					gfxid +=9;
				}
				break;
				case 6658: { //TODO 男龍
					gfxid +=1286;
				}
				break;
				case 6661: { //TODO 女龍
					gfxid +=1287;
				}
				break;
				case 6671: { //TODO 男幻
					gfxid +=1288;
				}
				break;
				case 6650: { //TODO 女幻
					gfxid +=1289;
				}
				break;
				}
				setTempCharGfx(gfxid);
				setGfxId(gfxid);

				if(special){
					setStatus(40); //TODO 魔法杖
					setAtkspeed(880); //TODO 魔法杖攻速
				}else{
					setStatus(4);
					setAtkspeed(900);
				}
				setPassispeed(640);
				//TODO 變形怪變身幻術師變成空手攻擊by9001183ex
				//setAtkspeed(900); // 正確な値がわからん
				for (final L1PcInstance pc : L1World.getInstance().getRecognizePlayer(this)) {
					pc.sendPackets(new S_RemoveObject(this));
					pc.removeKnownObject(this);
					pc.updateObject();
				}
			}
		}

	}

	@Override
	public void onPerceive(final L1PcInstance perceivedFrom) {
		perceivedFrom.addKnownObject(this);
		if (0 < getCurrentHp()) {
			if (getHiddenStatus() == HIDDEN_STATUS_SINK|| getHiddenStatus() == HIDDEN_STATUS_ICE) {
				perceivedFrom.sendPackets(new S_DoActionGFX(getId(),ActionCodes.ACTION_Hide));
			} else if (getHiddenStatus() == HIDDEN_STATUS_FLY) {
				perceivedFrom.sendPackets(new S_DoActionGFX(getId(),ActionCodes.ACTION_Moveup));
			}
			if(perceivedFrom.isGm()){
				perceivedFrom.sendPackets(new S_NPCPack(this,false));
			}else{
				perceivedFrom.sendPackets(new S_NPCPack(this));
			}
			onNpcAI(); // モンスターのＡＩを開始
			if (getBraveSpeed() == 1) { // ちゃんとした方法がわからない
				perceivedFrom.sendPackets(new S_SkillBrave(getId(), 1, 0));
			}
			perceivedFrom.sendPackets(new S_HPMeter(this)); //opqlo 血條顯示
		} else {
			perceivedFrom.sendPackets(new S_NPCPack(this));
		}
	}

	// ターゲットを探す
	public static int[][] _classGfxId = { { 0, 1 }, { 48, 61 }, { 37, 138 },{ 734, 1186 }, { 2786, 2796 } };
	@Override
	public void searchTarget() {
		// 攻擊目標搜尋
		L1PcInstance targetPlayer = null;
		//	L1MonsterInstance target = null;
		final L1Npc Npc = this.getNpcTemplate();
		final int mapId = this.getMapId();
		final int karma = Npc.getKarma();
		final int npcid = Npc.get_npcId();
		final int agofxid1 = Npc.is_agrogfxid1();
		final int agofxid2 = Npc.is_agrogfxid2();
		for (final L1Object obj : L1World.getInstance().getVisibleObjects(this)) {

			//for (final L1PcInstance pc : L1World.getInstance().getVisiblePlayer(this)) {
			if(obj instanceof L1PcInstance){
				final L1PcInstance pc = (L1PcInstance)obj;

				if (pc.getCurrentHp() <= 0) {
					continue;
				}
				if (pc.isDead()) {
					continue;
				}
				if (pc.isGhost()) {
					continue;
				}
				if (pc.isMonitor()) {
					continue;
				}
				if (pc.isGm()) {
					continue;
				}
				if (pc.isTeleport()){
					continue;
				}

				// 闘技場内は変身／未変身に限らず全てアクティブ

				if (mapId == 88 || mapId == 98 || mapId == 92 || mapId == 91
						|| mapId == 95) {
					if (!pc.isInvisble() || Npc.is_agrocoi()) { // インビジチェック
						targetPlayer = pc;
						break;
					}
				}
				final int Gfx = pc.getTempCharGfx();
				if (getNpcId() == 45600) { // カーツ
					if (pc.isCrown() || pc.isDarkelf()
							|| Gfx != pc.getClassId()) { // 未変身の君主、DEにはアクティブ
						targetPlayer = pc;
						break;
					}
				}

				// どちらかの条件を満たす場合、友好と見なされ先制攻撃されない。
				// ・モンスターのカルマがマイナス値（バルログ側モンスター）でPCのカルマレベルが1以上（バルログ友好）
				// ・モンスターのカルマがプラス値（ヤヒ側モンスター）でPCのカルマレベルが-1以下（ヤヒ友好）
				final int pckarma =  pc.getKarmaLevel();

				if ((karma < 0 && pckarma >= 1)
						|| (karma > 0 && pckarma <= -1)) {
					continue;
				}
				// 見棄てられた者たちの地 カルマクエストの変身中は、各陣営のモンスターから先制攻撃されない

				final boolean GfxOK = Gfx == 6035;
				if (Gfx == 6034 && karma < 0
						|| GfxOK
						&& karma > 0
						|| GfxOK
						&& npcid == 46070
						|| GfxOK
						&& npcid == 46072) {
					continue;
				}

				if (!Npc.is_agro() && !Npc.is_agrososc()
						&& agofxid1 < 0
						&& agofxid2 < 0) { // 完全なノンアクティブモンスター
					if (pc.getLawful() < -1000 || pc.hasSkillEffect(ATTRACT_MONSTER)) { // 邪惡狀態 或 擁有吸怪技能
						targetPlayer = pc;
						break;
					}
					continue;
				}

				if (!pc.isInvisble() || Npc.is_agrocoi()) { // インビジチェック
					if (pc.hasSkillEffect(67)) { // 変身してる
						if (Npc.is_agrososc()) { // 変身に対してアクティブ
							targetPlayer = pc;
							break;
						}
					} else if (Npc.is_agro()) { // アクティブモンスター
						targetPlayer = pc;
						break;
					}

					// 特定のクラスorグラフィックＩＤにアクティブ
					if (agofxid1 >= 0
							&& agofxid1 <= 4) { // クラス指定
						if (_classGfxId[agofxid1][0] == Gfx
								|| _classGfxId[agofxid1][1] == Gfx) {
							targetPlayer = pc;
							break;
						}
					} else if (Gfx == agofxid1) { // グラフィックＩＤ指定
						targetPlayer = pc;
						break;
					}

					if (agofxid2 >= 0&& agofxid2 <= 4) { // クラス指定
						if (_classGfxId[agofxid2][0] == Gfx
								|| _classGfxId[agofxid2][1] == Gfx) {
							targetPlayer = pc;
							break;
						}
					} else if (Gfx == agofxid2) { // グラフィックＩＤ指定
						targetPlayer = pc;
						break;
					}
				}
			}
		}
		if (targetPlayer != null) {
			_hateList.add(targetPlayer, 0);
			_target = targetPlayer;
			//TODO 怪物說話-怪物發現目標說話   use 丫傑 code 2/4
			MobTalk.getInstance().forL1MonsterTalking(this, 1);
			//TODO 怪物說話-怪物發現目標說話   use 丫傑 code 2/4

		}
		//		else if(this.getNpcId() == 2000263){// 仿暗黑哥布林 opqlo 
		//			if(_srctime ==0 ){
		//				_srctime = System.currentTimeMillis();
		//			}else{
		//				final long nowtime = System.currentTimeMillis();
		//				if(nowtime - _srctime >=20000){
		//					randomTeleport();
		//					_srctime = 0;
		//				}
		//			}
		//		}
		// 仿暗黑哥布林 opqlo 

		/*else if(target !=null){
			_hateList.add(target, 0);
			_target = target;
		}else if(mapId== 511 && npcid==45573){
			/*for(L1Object obj : L1World.getInstance().getObject()){
				if(obj.getMapId() != 511){
					continue;
				}*/
		/*if(obj instanceof L1MonsterInstance){
					final L1MonsterInstance tower = (L1MonsterInstance)obj;
					if(tower.getNpcTemplate().get_family()==116){//116李孝庭之塔
				_hateList.add(target, 0);
				_target = target;
				}
				}
			}*/

	}

	// 仿暗黑哥布林 opqlo 
	/**
	 * 隨機執行移動
	 * @param pc
	 * @param effectable
	 */
	//	private void randomTeleport() {
	//		try {
	//			// まだ本サーバのランテレ処理と違うところが結構あるような・・・
	//			final L1Location newLocation = this.getLocation().randomLocation(200, true);
	//			final int newX = newLocation.getX();
	//			final int newY = newLocation.getY();
	//			this.teleport(newX, newY, 5);
	//		} catch (final Exception e) {
	//			_log.log(Level.SEVERE, e.getLocalizedMessage() , e);
	//		}
	//	}
	// 仿暗黑哥布林 opqlo 

	// リンクの設定
	@Override
	public void setLink(L1Character cha) {
		if (cha != null && _hateList.isEmpty()) { // ターゲットがいない場合のみ追加
			_hateList.add(cha, 0);
			checkTarget();
		}
	}

	public L1MonsterInstance(L1Npc template) {
		super(template);
		_storeDroped = false;
	}

	@Override
	public void onNpcAI() {
		if (isAiRunning()) {
			return;
		}
		/*opqlo 掉落改寫
		if (!_storeDroped) // 無駄なオブジェクトＩＤを発行しないようにここでセット
		{
			DropTable.getInstance().setDrop(this, getInventory());
			getInventory().shuffle();
			_storeDroped = true;
		}
		 */
		this.getMap().getMobList().put(this.getId() ,this);
		setActived(false);
		startAI();
	}

	@Override
	public void onTalkAction(L1PcInstance pc) {
		int objid = getId();
		final L1NpcTalkData talking = NPCTalkDataTable.getInstance().getTemplate(getNpcTemplate().get_npcId());
		//		String htmlid = null;
		//		String[] htmldata = null;

		// html表示パケット送信
		//		if (htmlid != null) { // htmlidが指定されている場合
		//			if (htmldata != null) { // html指定がある場合は表示
		//				pc.sendPackets(new S_NPCTalkReturn(objid, htmlid, htmldata));
		//			} else {
		//				pc.sendPackets(new S_NPCTalkReturn(objid, htmlid));
		//			}
		//		} else {
		if (pc.getLawful() < -1000) { // プレイヤーがカオティック
			pc.sendPackets(new S_NPCTalkReturn(talking, objid, 2));
		} else {
			pc.sendPackets(new S_NPCTalkReturn(talking, objid, 1));
		}
		//		}
	}

	@Override
	public void onAction(final L1PcInstance pc) {
		if (getCurrentHp() > 0 && !isDead()) {


			L1AttackModel attack = new L1AttackPc(pc, this);
			boolean isCounterBarrier = false;
			if (attack.calcHit()) {
				if (hasSkillEffect(L1SkillId.NPC_COUNTER_BARRIER)) {
					L1Magic magic = new L1Magic(this, pc);

					boolean isProbability = magic.calcProbabilityMagic(L1SkillId.NPC_COUNTER_BARRIER);
					magic = null;
					boolean isShortDistance = attack.isShortDistance();
					if (isProbability && isShortDistance) {
						isCounterBarrier = true;
						this.broadcastPacketAll(new S_DoActionGFX(pc.getId(), ActionCodes.ACTION_Damage));
					}
				}

				if (!isCounterBarrier) {
					attack.calcDamage();
					attack.calcStaffOfMana();
					attack.addChaserAttack();
				}
			}
			if (isCounterBarrier) {
				attack.actionCounterBarrier();
				attack.commitCounterBarrier();

			} else {
				attack.action();
				attack.commit();
			}
			attack = null;
		}
	}

	@Override
	public void ReceiveManaDamage(final L1Character attacker,final int mpDamage) { // 攻撃でＭＰを減らすときはここを使用
		if (mpDamage > 0 && !isDead()) {
			// int Hate = mpDamage / 10 + 10; // 注意！計算適当 ダメージの１０分の１＋ヒットヘイト１０
			// setHate(attacker, Hate);
			setHate(attacker, mpDamage);

			onNpcAI();

			if (attacker instanceof L1PcInstance) { // 仲間意識をもつモンスターのターゲットに設定
				serchLink((L1PcInstance) attacker, getNpcTemplate().get_family());
			}

			setCurrentMp(Math.max(0, getCurrentMp() - mpDamage));
		}
	}
	private int _dropMoneyCount =Random.nextInt(30)+1;


	@Override
	public void receiveDamage(final L1Character attacker,int damage) { // 攻撃でＨＰを減らすときはここを使用
		if (getCurrentHp() > 0 && !isDead()) {
			if (getHiddenStatus() == HIDDEN_STATUS_SINK
					|| getHiddenStatus() == HIDDEN_STATUS_FLY) {
				return;
			}
			if(this.getDmgReduceRate()==100 && damage >1){
				damage = 1;
			}
			if (damage >= 0) {
				if (!(attacker instanceof L1EffectInstance)) { // FWはヘイトなし
					setHate(attacker, damage);
				}
			}
			if (damage > 0) {
				removeSkillEffect(L1SkillId.FOG_OF_SLEEPING);
			}

			onNpcAI();

			if (attacker instanceof L1PcInstance) { // 仲間意識をもつモンスターのターゲットに設定
				serchLink((L1PcInstance) attacker, getNpcTemplate().get_family());

				if(damage >0){
					final	L1PcInstance player = (L1PcInstance) attacker;
					player.setPetTarget(this);

					final int npcid = getNpcTemplate().get_npcId();
					switch(npcid){
					case 45681: case 45682: case 45683: case 45684:// opqlo 四大龍拉人
					case 710046: case 2000254:	//神恩年獸[馬]
					case	2000265: //[BOSS]聖誕魔人
						recall(player);
						break;
					case 81305: case 81306:	//TODO GM測試用怪物
						String Msg = " 剩餘" + getCurrentHp();
						if (damage >= getCurrentHp()) {
							Msg = " 剩餘0";
						}
						player.sendPackets(new S_TrueTarget(getId(), player.getId(), damage + Msg));
						break;
					case 2000263: // 仿暗黑哥布林 opqlo 
						if(_dropMoneyCount >0){
							final L1ItemInstance item = ItemTable.getInstance().createItem(90308);
							item.setCount(Random.nextInt(2)+1);
							L1World.getInstance().getInventory(getX(),getY(),getMapId()).storeItem(item);
//							this.getInventory().tradeItem(item, item.getCount(), L1World.getInstance().getInventory(getX(),getY(),getMapId()));
//							this.getInventory().removeItem(item, item.getCount());
//							System.out.println(this.getInventory().findItemIdAndGetCount(90308));
							turnOnOffLight();
							_dropMoneyCount--;
						}

						break;
					}
				}
			}


			final int newHp = getCurrentHp() - damage;
			if (newHp > 0) {
				setCurrentHp(newHp);
				hide();
			}else if (newHp <= 0 && !isDead()) {
				final int transformId = getNpcTemplate().getTransformId();
				// 変身しないモンスター
				if (transformId == -1) {
					setCurrentHpDirect(0);
					setDead(true);
					setStatus(ActionCodes.ACTION_Die);
					openDoorWhenNpcDied(this);
					final Death death = new Death(this,attacker);//opqlo 掉落改寫
					GeneralThreadPool.getInstance().execute(death);
					// Death(attacker);
				} else { // 変身するモンスター
					// distributeExpDropKarma(attacker);
					transform(transformId);
				}
			}

		} else if (!isDead()) { // 念のため
			setDead(true);
			this.getMap().getMobList().remove(this.getId());
			setStatus(ActionCodes.ACTION_Die);
			final Death death = new Death(this,attacker);//opqlo 掉落改寫
			GeneralThreadPool.getInstance().execute(death);
			// Death(attacker);
		}
		/* double endTime, totTime;

		//把要測試的程式放到endTime前面
        endTime = System.currentTimeMillis();
        //取得程式結束的時間
        totTime = endTime - GameServer.startTime;
        //計算經過多久
        System.out.println("Using Time: " + totTime/1000+" sec");
        System.out.println("Using Time: " + totTime+" ms");*/
	}

	private static void openDoorWhenNpcDied(final L1NpcInstance npc) {
		final int[] npcId = { 46143, 46144, 46145, 46146, 46147, 46148, 46149, 46150,46151, 46152 };
		final int[] doorId = { 5001, 5002, 5003, 5004, 5005, 5006, 5007, 5008, 5009,5010 };

		for (int i = 0; i < npcId.length; i++) {
			if (npc.getNpcTemplate().get_npcId() == npcId[i]) {
				openDoorInCrystalCave(doorId[i]);
			}
		}
	}

	private static void openDoorInCrystalCave(final int doorId) {
		for (final L1Object object : L1World.getInstance().getObject()) {
			if (object instanceof L1DoorInstance) {
				final L1DoorInstance door = (L1DoorInstance) object;
				if (door.getDoorId() == doorId) {
					door.open();
				}
			}
		}
	}

	/**
	 * 距離が5以上離れているpcを距離3～4の位置に引き寄せる。
	 * 
	 * @param pc
	 */
	private void recall(final L1PcInstance pc) {
		if (getMapId() != pc.getMapId()) {
			return;
		}
		if (getLocation().getTileLineDistance(pc.getLocation()) > 2) {
			for (int count = 0; count < 10; count++) {
				final L1Location newLoc = getLocation().randomLocation(1, 1, false);
				if (glanceCheck(newLoc.getX(), newLoc.getY())) {
					L1Teleport.teleportByMagic(pc, newLoc.getX(), newLoc.getY(),getMapId(), 5, true);
					break;
				}
			}
		}
	}

	//	/**
	//	 * 怪物血條封包 By opqlo
	//	 * @param 
	//	 * */
	//
	//	private void sendHpBar(final boolean open){
	//
	//		if(!getHateList().isEmpty()){
	//
	//			ArrayList<L1Character> targetList = _hateList.toTargetArrayList();
	//			ArrayList<Integer> hateList = _hateList.toHateArrayList();
	//			L1Character cha =null;
	//			L1PcInstance pc = null;
	//			if (targetList.size() == hateList.size()) {
	//				for (int s = hateList.size() - 1; s >= 0; s--) {
	//					cha = (L1Character)targetList.get(s);
	//					if (cha != null && !cha.isDead()) {
	//						if (cha instanceof L1PcInstance) {
	//							pc = (L1PcInstance)cha;
	//						}else if (cha instanceof L1PetInstance){
	//							pc = (L1PcInstance) ((L1PetInstance)cha).getMaster();
	//						}else if(cha instanceof L1SummonInstance){
	//							pc = (L1PcInstance) ((L1SummonInstance)cha).getMaster();
	//						}
	//						if(pc!=null){
	//							if(open){
	//								pc.sendPackets(new S_HPMeter(this));
	//							}else{
	//								pc.sendPackets(new S_HPMeter(getId(), 0xFF));
	//							}
	//						}
	//					}
	//				}
	//			}
	//		}
	//	}

	@Override
	public void setCurrentHp(final int i) {

		setCurrentHpDirect(Math.min(getMaxHp(), i ));

		if (getMaxHp() > getCurrentHp()) {
			startHpRegeneration();
		}
		//		sendHpBar(true);//opqlo 打怪顯示血條
	}

	@Override
	public void setCurrentMp(final int i) {

		setCurrentMpDirect(Math.min(getMaxMp(), i ));

		if (getMaxMp() > getCurrentMp()) {
			startMpRegeneration();
		}
	}

	private static final int[] allBuffSkill = {  
		L1SkillId.PHYSICAL_ENCHANT_DEX,
		L1SkillId.PHYSICAL_ENCHANT_STR, 
		L1SkillId.BERSERKERS,
		L1SkillId.IMMUNE_TO_HARM, 
		L1SkillId.ADVANCE_SPIRIT, 
		L1SkillId.VENOM_RESIST,
		L1SkillId.BRAVE_AURA, 
		L1SkillId.RESIST_MAGIC, 
		L1SkillId.AQUA_PROTECTER, 
		L1SkillId.WATER_LIFE, 
		L1SkillId.SOUL_OF_FLAME, 
		L1SkillId.MIRROR_IMAGE,
		L1SkillId.ILLUSION_OGRE,
		L1SkillId.ILLUSION_LICH,
		L1SkillId.PATIENCE,
		L1SkillId.ILLUSION_DIA_GOLEM,
		L1SkillId.INSIGHT,
		L1SkillId.DRAGON_SKIN
	}; 

	class Death implements Runnable {
		L1Character _lastAttacker;
		L1NpcInstance _npc;

		public Death(final L1NpcInstance npc ,final L1Character lastAttacker) {
			_lastAttacker = lastAttacker;
			_npc = npc;
		}

		// @Override
		@Override
		public void run() {

			try{
				//************************//opqlo 掉落改寫
				if(!_storeDroped){
					_storeDroped = true;
					if(getInventory() !=null){
						final SetDropExecutor setdrop = new SetDrop();
						setdrop.setDrop(_npc , _lastAttacker , getInventory());
						getInventory().shuffle();
					}
				}
				//************************//
			}catch(final Exception e){
				_log.log(Level.SEVERE, e.getLocalizedMessage(), e);
			}
			setDeathProcessing(true);
			setCurrentHpDirect(0);
			setDead(true);//
			final int action_die = ActionCodes.ACTION_Die;
			setStatus(action_die);

			getMap().setPassable(getLocation(), true);

			broadcastPacketAll(new S_DoActionGFX(getId(), action_die));

			startChat(CHAT_TIMING_DEAD);

			distributeExpDropKarma(_lastAttacker);
			giveUbSeal();

			setDeathProcessing(false);

			setExp(0);
			setKarma(0);
			allTargetClear();
			startDeleteTimer();
			//opqlo 副本用
			if(isQuestMob){
				TreasureMaster.getInstance().RemoveTreasure(getMapId(),getNpcId());
				return;
			}else if(getNpcId()==2000263){
				String name ;
				if(_lastAttacker instanceof L1PcInstance){
					name = _lastAttacker.getName();
				}else if(_lastAttacker instanceof L1SummonInstance || _lastAttacker instanceof L1PetInstance){
					L1NpcInstance pet = (L1NpcInstance)_lastAttacker;
					if(pet.getMaster() !=null){
						name = pet.getMaster().getName();
					}else{
						name = "";
					}
				}else{
					name = "";
				}
				goblinThread.getInstance().npcIsDead(_npc , name);
			}
			if(getNpcId()==710065){// opqlo 巫術之眼
				if(_lastAttacker instanceof L1PcInstance){
					try{
						final L1PcInstance pc = (L1PcInstance) _lastAttacker;
						pc.setCurrentHp(pc.getMaxHp());
						pc.setCurrentMp(pc.getMaxMp());
						final L1SkillUse skilluse = new L1SkillUse();
						L1Skills skill;

						for (final int element : allBuffSkill) {
							if(pc.getNetConnection() == null){
								break;
							}
							skill = SkillsTable.getInstance().getTemplate(element);
							skilluse.handleCommands(pc, element,pc.getId(), pc.getX(), pc.getY(), null, skill.getBuffDuration(),L1SkillUse.TYPE_GMBUFF);
						}
						return;
					}catch(Exception e){
						_log.log(Level.SEVERE, e.getLocalizedMessage() , e);
					}
				}
			}
			/*		if(getNpcId()== 710035 ){ // opqlo 萬聖節 貴利高吸血佬
				if(_lastAttacker instanceof L1PcInstance){
					final L1PcInstance pc = (L1PcInstance) _lastAttacker;
					L1PolyMorph.doPoly(pc, 2448, 7200,L1PolyMorph.MORPH_BY_GM);//TODO 白金光圈刺客
				final L1SkillUse skilluse = new L1SkillUse();
				L1Skills skill;
				final int[] allBuffSkill = {  L1SkillId.DECREASE_WEIGHT, 
						L1SkillId.PHYSICAL_ENCHANT_DEX,
						L1SkillId.PHYSICAL_ENCHANT_STR, 
						L1SkillId.BLESS_WEAPON, 
						L1SkillId.BERSERKERS,
						L1SkillId.IMMUNE_TO_HARM, 
						L1SkillId.ADVANCE_SPIRIT, 
						L1SkillId.REDUCTION_ARMOR, 
						L1SkillId.SOLID_CARRIAGE, 
						L1SkillId.ENCHANT_VENOM, 
						L1SkillId.BURNING_SPIRIT, 
						L1SkillId.VENOM_RESIST,
						L1SkillId.DOUBLE_BRAKE, 
						L1SkillId.UNCANNY_DODGE, 
						L1SkillId.DRESS_EVASION, 
						L1SkillId.GLOWING_AURA,
						L1SkillId.BRAVE_AURA, 
						L1SkillId.RESIST_MAGIC, 
						L1SkillId.CLEAR_MIND, 
						L1SkillId.ELEMENTAL_PROTECTION,
						L1SkillId.AQUA_PROTECTER, 
						L1SkillId.BURNING_WEAPON, 
						L1SkillId.IRON_SKIN, 
						L1SkillId.WATER_LIFE, 
						L1SkillId.ELEMENTAL_FIRE, 
						L1SkillId.SOUL_OF_FLAME, 
						L1SkillId.ADDITIONAL_FIRE,
						L1SkillId.MIRROR_IMAGE,
						L1SkillId.ILLUSION_OGRE,
						L1SkillId.ILLUSION_LICH,
						L1SkillId.CONCENTRATION,
						L1SkillId.PATIENCE,
						L1SkillId.ILLUSION_DIA_GOLEM,
						L1SkillId.INSIGHT,
						L1SkillId.ILLUSION_AVATAR,
						L1SkillId.MORTAL_BODY,
						L1SkillId.DRAGON_SKIN};

				for (final int element : allBuffSkill) {
					 skill = SkillsTable.getInstance().getTemplate(element);
					skilluse.handleCommands(pc, element,pc.getId(), pc.getX(), pc.getY(), null, skill.getBuffDuration(),L1SkillUse.TYPE_GMBUFF);
				}
			}
				return;
			}
			 // opqlo 萬聖節 貴利高吸血佬*/
		}
	}

	private void distributeExpDropKarma(L1Character lastAttacker) {
		if (lastAttacker == null) {
			return;
		}
		L1PcInstance pc = null;
		if (lastAttacker instanceof L1PcInstance) {
			pc = (L1PcInstance) lastAttacker;
		} else if (lastAttacker instanceof L1PetInstance) {
			pc = (L1PcInstance) ((L1PetInstance) lastAttacker).getMaster();
		} else if (lastAttacker instanceof L1SummonInstance) {
			pc = (L1PcInstance) ((L1SummonInstance) lastAttacker).getMaster();
		}
		//		sendHpBar(false);//opqlo 打怪顯示血條
		if (pc != null) {

			//TODO 怪物說話-怪物死亡說話 use 丫傑 code 4/4
			MobTalk.getInstance().forL1MonsterTalking(this, 3);
			//TODO 怪物說話-怪物死亡說話 use 丫傑 code 4/4
			if (isDead()) {
				//TODO 怪物強化系統by missu0524
				if (MonsterEnhanceTable.getInstance().getTemplate(getNpcId()) != null) {
					final 	L1MonsterEnhanceInstance mei = MonsterEnhanceTable.getInstance().getTemplate(getNpcId());
					if (mei.getDcEnhance() != 0) {
						mei.setCurrentDc(mei.getCurrentDc()+1);
						MonsterEnhanceTable.getInstance().save(mei);
						if ((mei.getCurrentDc()%mei.getDcEnhance()) == 0) {                                        
							L1World.getInstance().broadcastPacketToAll(new S_SystemMessage(getName()+" 有不敗的精神，躲起來修練後，變的更強了!"));
						}
					}
				}
				//TODO 怪物強化系統by missu0524
				distributeDrop();
				giveKarma(pc);

				final ArrayList<L1Character> targetList = _hateList.toTargetArrayList();
				final ArrayList<Integer> hateList = _hateList.toHateArrayList();
				CalcExp.calcExp(pc, getId(), targetList, hateList, getExp());
				// 死亡した場合はドロップとカルマも分配、死亡せず変身した場合はEXPのみ
			}

		} else if (lastAttacker instanceof L1EffectInstance) { // FWが倒した場合
			final ArrayList<L1Character> targetList = _hateList.toTargetArrayList();
			final ArrayList<Integer> hateList = _hateList.toHateArrayList();
			// ヘイトリストにキャラクターが存在する
			if (hateList.size() != 0) {
				// 最大ヘイトを持つキャラクターが倒したものとする
				int maxHate = 0;
				for (int i = hateList.size() - 1; i >= 0; i--) {
					if (maxHate < (hateList.get(i))) {
						maxHate = (hateList.get(i));
						lastAttacker = targetList.get(i);
					}
				}
				if (lastAttacker instanceof L1PcInstance) {
					pc = (L1PcInstance) lastAttacker;
				} else if (lastAttacker instanceof L1PetInstance) {
					pc = (L1PcInstance) ((L1PetInstance) lastAttacker).getMaster();
				} else if (lastAttacker instanceof L1SummonInstance) {
					pc = (L1PcInstance) ((L1SummonInstance) lastAttacker).getMaster();
				}
				if (pc != null) {
					final long exp = getExp();
					CalcExp.calcExp(pc, getId(), targetList, hateList, exp);
					// 死亡した場合はドロップとカルマも分配、死亡せず変身した場合はEXPのみ
					if (isDead()) {
						distributeDrop();
						giveKarma(pc);
					}
				}
			}
		}
	}

	private void distributeDrop() {
		try {
			final 	ArrayList<L1Character> dropTargetList = _dropHateList.toTargetArrayList();
			final 	ArrayList<Integer> dropHateList = _dropHateList.toHateArrayList();
			final 	int npcId = getNpcTemplate().get_npcId();
			/**仿暗黑哥布林 opqlo*/
			if(npcId == 2000263 ){
				int x = getX()-4;
				int y = getY()+4;
				final short mapid = getMapId();
				while (y>getY()-4){
					Thread.sleep(1);
					if(getMap().isPassable(x, y)){
						L1ItemInstance item = ItemTable.getInstance().createItem(90308);
						item.setCount(Random.nextInt(15)+1);
						L1World.getInstance().getInventory(x,y,mapid).storeItem(item);
//						this.getInventory().tradeItem(item, item.getCount(), L1World.getInstance().getInventory(x,y,mapid));
						turnOnOffLight();
					}
					if(x == getX()+4){
						if(y> getY()-4){
							y-=2;
						}else{
							break;
						}
						x = getX()-4;
					}else{
						x+=2;
					}
				}
				/**聖誕節活動 馴鹿 opqlo*/
			}else if(npcId == 2000264){
				XMas.addXmasCount();
			}
			/**仿暗黑哥布林 opqlo*/
			if (npcId != 45640 || (npcId == 45640 && getTempCharGfx() == 2332)) {
				//				DropTable.getInstance().dropShare(L1MonsterInstance.this,dropTargetList, dropHateList);
				final DropShareExecutor dropShareExecutor = new DropShare();
				dropShareExecutor.dropShare(L1MonsterInstance.this, dropTargetList, dropHateList);
			}
		} catch (Exception e) {
			_log.log(Level.SEVERE, e.getLocalizedMessage(), e);
		}
	}

	private void giveKarma(final L1PcInstance pc) {
		int karma = getKarma();
		if (karma != 0) {
			final int karmaSign = Integer.signum(karma);
			final int pcKarmaLevel = pc.getKarmaLevel();
			final int pcKarmaLevelSign = Integer.signum(pcKarmaLevel);
			// カルマ背信行為は5倍
			if (pcKarmaLevelSign != 0 && karmaSign != pcKarmaLevelSign) {
				karma *= 5;
			}
			// カルマは止めを刺したプレイヤーに設定。ペットorサモンで倒した場合も入る。
			pc.addKarma((int) (karma * Config.RATE_KARMA));
		}
	}

	private void giveUbSeal() {
		if (getUbSealCount() != 0) { // UBの勇者の証
			final L1UltimateBattle ub = UBTable.getInstance().getUb(getUbId());
			if (ub != null) {
				for (final L1PcInstance pc : ub.getMembersArray()) {
					if (pc != null && !pc.isDead() && !pc.isGhost()) {
						final L1ItemInstance item = pc.getInventory().storeItem(41402, getUbSealCount());
						pc.sendPackets(new S_ServerMessage(403, item.getLogName())); // %0を手に入れました。
					}
				}
			}
		}
	}
	/*
	private boolean is_storeDroped() { // opqlo 掉落改寫
		return _storeDroped;
	}
	 */
	public void set_storeDroped(final boolean flag) {
		_storeDroped = flag;
	}

	private int _ubSealCount = 0; // UBで倒された時、参加者に与えられる勇者の証の個数

	public int getUbSealCount() {
		return _ubSealCount;
	}

	public void setUbSealCount(final int i) {
		_ubSealCount = i;
	}

	private int _ubId = 0; // UBID

	public int getUbId() {
		return _ubId;
	}

	public void setUbId(final int i) {
		_ubId = i;
	}

	private void hide() {

		switch(getNpcTemplate().get_npcId()){
		case 45061://死亡騎士
		case 45161://史巴托
		case 45181://史巴托
		case 45455://殘暴的史巴托
			if (getMaxHp() / 3 > getCurrentHp()) {
				if (1 > Random.nextInt(10)) {
					setHide(ActionCodes.ACTION_AntharasHide , HIDDEN_STATUS_SINK , 13);
				}
			}
			break;
			//TODO 思克巴瀕死傳送延遲by9001183ex
		case 45393: case 45394: case 45400: //思克巴
		case 45080: //弱化思克巴
		case 45391: //炎魔的思克巴
		case 45451: case 46156: //思克巴女皇
		case 45110: //受封印的思克巴女皇
		case 45450: //炎魔的思克巴女皇
		case 45580: //暗黑思克巴女皇
			if (getMaxHp() / 3 > getCurrentHp() //TODO hp低於3/1
					&& !hasSkillEffect(SUCCBUS_TELEPORT_DELAY) //TODO 再瞬移的延遲時間
					&& getCurrentMp() > 20) { //TODO 剩餘mp大於20點
				if (Config.DYINH_TRANSMISSINN >= Random.nextInt(100) + 1) {
					final 	L1Location newLocation = getLocation().randomLocation(3, true);
					if (nearTeleport(newLocation.getX(), newLocation.getY()) == true) {
						getMap().setPassable(getX(), getY(),true);
						setSkillEffect(SUCCBUS_TELEPORT_DELAY, 1500); //TODO 給予再瞬移的延遲時間1秒半
						setCurrentMp(getCurrentMp() - 20); //TODO 扣除20點mp
						broadcastPacketAll(new S_NPCPack(this)); //TODO 重新發布NPCPack避免傳送後變隱形
						getMap().setPassable(getX(), getY(),false);
						return;
					}
				}
			}
			//TODO 思克巴瀕死傳送延遲by9001183ex
			break;
		case 45682: //安塔瑞斯
			if (getMaxHp() / 3 > getCurrentHp()) {
				if (1 > Random.nextInt(50)) {
					setHide(ActionCodes.ACTION_AntharasHide , HIDDEN_STATUS_SINK , 20);
				}
			}
			break;

		case 45681: //林德拜爾
			if (getMaxHp() / 3 > getCurrentHp()) {
				if (1 > Random.nextInt(50)) {
					setHide(ActionCodes.ACTION_Moveup , HIDDEN_STATUS_FLY , 11);
				}
			}
			break;

		case 45067: //弱化哈維
		case 45264: //火焰之影親衛兵
		case 45452: //哈維
		case 45090: //弱化格利芬
		case 45321: case 45445: //格利芬
			if (getMaxHp() / 3 > getCurrentHp()) {
				if (1 > Random.nextInt(10)) {
					setHide(ActionCodes.ACTION_Moveup , HIDDEN_STATUS_FLY , 4);
				}
			}
			break;
		case 46107: case 46108: //底比斯 曼陀羅草
			if (getMaxHp() / 4 > getCurrentHp()) {
				if (1 > Random.nextInt(10)) {
					setHide(ActionCodes.ACTION_Hide , HIDDEN_STATUS_SINK , 13);
				}
			}
			break;
		}

	}

	private void setHide(final int Action ,final int HidenStatus ,final int Status){
		if(Action >0){
			allTargetClear();
			setHiddenStatus(HidenStatus);
			broadcastPacketAll(new S_DoActionGFX(getId(),Action));
			setStatus(Status);
			broadcastPacketAll(new S_NPCPack(this));
		}else{
			setHiddenStatus(HidenStatus);
			setStatus(Status);
		}
	}

	public void initHide() {
		// 出現直後の隠れる動作
		// 潜るMOBは一定の確率で地中に潜った状態に、
		// 飛ぶMOBは飛んだ状態にしておく
		switch(getNpcTemplate().get_npcId()){
		case 45061://死亡騎士
		case 45161://史巴托
		case 45181://史巴托
		case 45455://殘暴的史巴托
			if (1 > Random.nextInt(3)) {
				setHide(0 , HIDDEN_STATUS_SINK , 13);
			}
			break;
		case 45045: // 弱化石頭高崙
		case 45126: case 45134: //石頭高崙
		case 45281: //奇岩 石頭高崙
			if (1 > Random.nextInt(3)) {
				setHide(0 , HIDDEN_STATUS_SINK , 4);
			}
			break;
		case 45067: //弱化哈維
		case 45264: //火焰之影親衛兵
		case 45452: //哈維
		case 45090: //弱化格利芬
		case 45321: case 45445: //格利芬
			setHide(0 , HIDDEN_STATUS_FLY , 4);
			break;
		case 45681: //林德拜爾
			setHide(0 , HIDDEN_STATUS_FLY , 11);
			break;
		case 46107: case 46108: //底比斯 曼陀羅草
			if (1 > Random.nextInt(3)) {
				setHide(0 , HIDDEN_STATUS_SINK , 13);
			}
			break;
		case 46125: //鋼鐵高崙
		case 46126: //萊肯
		case 46127: //歐熊
			setHide(0 , HIDDEN_STATUS_ICE , 4);
			break;
		}

	}

	public void initHideForMinion(final L1NpcInstance leader) {
		// グループに属するモンスターの出現直後の隠れる動作（リーダーと同じ動作にする）
		final int npcid = getNpcTemplate().get_npcId();
		if (leader.getHiddenStatus() == HIDDEN_STATUS_SINK) {
			if (npcid == 45061 // カーズドスパルトイ
					|| npcid == 45161 // スパルトイ
					|| npcid == 45181 // スパルトイ
					|| npcid == 45455) { // デッドリースパルトイ
				setHiddenStatus(HIDDEN_STATUS_SINK);
				setStatus(13);
			} else if (npcid == 45045 // クレイゴーレム
					|| npcid == 45126 // ストーンゴーレム
					|| npcid == 45134 // ストーンゴーレム
					|| npcid == 45281) { // ギランストーンゴーレム
				setHiddenStatus(HIDDEN_STATUS_SINK);
				setStatus(4);
			} else if (npcid == 46107 // テーベ マンドラゴラ(白)
					|| npcid == 46108) { // テーベ マンドラゴラ(黒)
				setHiddenStatus(HIDDEN_STATUS_SINK);
				setStatus(13);
			}
		} else if (leader.getHiddenStatus() == HIDDEN_STATUS_FLY) {
			if (npcid == 45067 // バレーハーピー
					|| npcid == 45264 // ハーピー
					|| npcid == 45452 // ハーピー
					|| npcid == 45090 // バレーグリフォン
					|| npcid == 45321 // グリフォン
					|| npcid == 45445) { // グリフォン
				setHiddenStatus(HIDDEN_STATUS_FLY);
				setStatus(4);
			} else if (npcid == 45681) { // リンドビオル
				setHiddenStatus(HIDDEN_STATUS_FLY);
				setStatus(11);
			}
		} else if (npcid >= 46125 && npcid <= 46128) {
			setHiddenStatus(L1NpcInstance.HIDDEN_STATUS_ICE);
			setStatus(4);
		}
	}

	@Override
	protected void transform(final int transformId) {
		super.transform(transformId);

		// DROPの再設定
		getInventory().clearItems();
		/* opqlo 掉落改寫
		DropTable.getInstance().setDrop(this, getInventory());
		getInventory().shuffle();
		 */
	}
}
